package com.guigu.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guigu.gateway.feign.AuthFeign;
import com.guigu.gateway.pojo.AuthResource;
import com.guigu.gateway.utils.JwtUtils;
import com.guigu.gateway.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;

/**
 * @ClassName:AuthGlobalFilter
 * @Description://TODO
 * @Author:taoLong
 * @Date:2021/12/8:19:51 星期三
 * @email:2391611102@qq.com or 2391611102qq@gmail.com
 */
@Component
@Slf4j

public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private AuthFeign authFeign;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        /**
         * 如果是登录则放行
         */
        if (path.contains("user/login")
                || path.contains("auth/auth-user/findByToken")
                || path.contains("v2/api-docs")
                || path.contains("auth/auth/auth-menu/queryAllMenuRouter")
                || path.contains("user/logout")
                || path.contains("base/dashboard/getDashboard")
                || path.contains("/api/ucenter/wx")
        ||path.contains("tms-work/task-pickup-dispatch/list/pickup")
        ||path.contains("task-pickup-dispatch/updateStatusById")
        ||path.contains("base/global-user/login")
        ||path.contains("driver-job/")
        ||path.contains("oss/oss/getSignature")) {
            log.info("路径为:{}", path);
            return chain.filter(exchange);
        }
        /**
         * 获取送来的请求头信息
         */

        String token = request.getHeaders().getFirst("token");
        log.info("token信息为:{}", token);
        if (StringUtils.isEmpty(token)) {
            return out(response, new R("2002", "未登录,请登录!", false));
        }
        if (!JwtUtils.checkToken(token)) {
            return out(response, new R("2009", "会话已过期!", false));
        }

        /**
         * 判断当前请求是否在需要授权的资源内
         */

        String id = JwtUtils.getMemberIdByJwtToken(token);
        log.info("token字符串为:{}", id);

        String method = request.getMethod().toString();
        log.info("请求方法为:{}", method);


        /**
         * 将请求方法和uri拼接起来
         */
        StringBuffer resourceCode = new StringBuffer();
        resourceCode.append(method);
        resourceCode.append(path);
        if (checkURI(resourceCode.toString())) {
            log.info("该uri在数据中!");
            if (checkUriByUserId(resourceCode.toString(), id)) {
                log.info("该uri在该用户权限范围内");
                return chain.filter(exchange);
            } else {
                log.error("该uri不在用户范围内");
                return out(response, new R("2006", "你无该权限!", false));
            }
        } else {
           /* log.error("该uri不在数据范围内");
            return out(response, new R("2006", "你无该权限!", false));*/
            return chain.filter(exchange);
        }

    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 当认证或鉴权失败时输出到前端
     *
     * @param response
     * @param r
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, R r) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            String resultStr = mapper.writeValueAsString(r);
            DataBuffer dataBuffer = response.bufferFactory().wrap(resultStr.getBytes(StandardCharsets.UTF_8));
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Mono.just(dataBuffer));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 查看该url是否在我们的权限库内
     *
     * @param uri
     * @return
     */
    private boolean checkURI(String uri) {
        log.info("uri为:{}", uri);
        R<List<AuthResource>> r = authFeign.queryAll();
        if (CollectionUtils.isEmpty(r.getData())) {
            return false;
        }
        for (AuthResource resource : r.getData()) {
            if (uri.startsWith(resource.getCode())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 查看该url是否在该用户权限范围内
     *
     * @param id  用户Id
     * @param uri 统一资源标识符
     * @return
     */
    private boolean checkUriByUserId(String uri, String id) {
        R<Set<AuthResource>> r = authFeign.queryResourcesBasedOnUserId(id);
        if (CollectionUtils.isEmpty(r.getData())) {
            return false;
        }
        for (AuthResource resource : r.getData()) {
            if (uri.startsWith(resource.getCode())) {
                return true;
            }
        }
        return false;
    }
}
